﻿#region Description
/*  File Name:      InputManager.cs
 *  Author:         Adam Goodchild
 *  
 *  Description:    This class Manages the Input States for the Minimize Engine.
 *                  It can handle Input from the following:
 *                   - Keyboard
 *                   - Mouse
 *                   - GamePad
 *                  This class also handles Input for a number of higher level functions
 *                  These Include:
 *                   - Menu Select
 *                   - Menu Cancel
 *                   - Menu Move Up
 *                   - Menu Move Down
 *                   - Paused Game                   
 *  
 *  Sources:         The Idea for this class was gained from the following source(s):
 *                  Microsoft XNA Creators Club - Game State Management Sample
*/
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
#endregion

namespace MinimizeEngine.Managers
{
    public class InputManager
    {
        #region Fields

        public const int m_iMaxNumberOfInputs = 4;

        public readonly KeyboardState[] mCurrentKeyBoardStates;
        public readonly GamePadState[] mCurrentGamePadStates;
        public readonly KeyboardState[] mLastKeyboardStates;
        public readonly GamePadState[] mLastGamePadStates;

        public readonly bool[] m_bGamePadWasConnected;

        #endregion

        #region Initialization

        /// <summary>
        /// Construct a new Input Manager
        /// </summary>
        public InputManager()
        {
            //Current Input States
            mCurrentKeyBoardStates = new KeyboardState[m_iMaxNumberOfInputs];
            mCurrentGamePadStates = new GamePadState[m_iMaxNumberOfInputs];

            //Last Input States
            mLastKeyboardStates = new KeyboardState[m_iMaxNumberOfInputs];
            mLastGamePadStates = new GamePadState[m_iMaxNumberOfInputs];

            m_bGamePadWasConnected = new bool[m_iMaxNumberOfInputs];
        }

        #endregion

        #region Update

        /// <summary>
        /// Update the Input States.
        /// Reads the latest states for the Keyboard and GamePad.
        /// </summary>
        public void Update()
        {
            for(int i = 0; i < m_iMaxNumberOfInputs; i++)
            {
                //Save the Last Input States
                mLastKeyboardStates[i] = mCurrentKeyBoardStates[i];
                mLastGamePadStates[i] = mLastGamePadStates[i];

                //And now get the Current Input States
                mCurrentKeyBoardStates[i] = Keyboard.GetState((PlayerIndex)i);
                mCurrentGamePadStates[i] = GamePad.GetState((PlayerIndex)i);

                //Now we keep track if a GamePad has ever been Connected.
                //This allows us to detect if it has been Unplugged.
                if(mCurrentGamePadStates[i].IsConnected)
                {
                    m_bGamePadWasConnected[i] = true;
                }
            }
        }

        #endregion

        #region Key/Button Checks
        /// <summary>
        /// Check to see if a new key was pressed during the current Update.
        /// controllingPlayer specifies which player is checked for Input.
        /// playerIndex specifies which player pressed it.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="controllingPlayer"></param>
        /// <param name="playerIndex"></param>
        /// <returns></returns>
        public bool IsNewKeyPress(Keys key, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
        {
            if (controllingPlayer.HasValue)
            {
                //Read Input from the Specified Controlling Player
                playerIndex = controllingPlayer.Value;

                int i = (int)playerIndex;

                return (mCurrentKeyBoardStates[i].IsKeyDown(key) &&
                    mLastKeyboardStates[i].IsKeyUp(key));
            }
            else
            {
                //Otherwise, we can accept Input from any Player
                return (IsNewKeyPress(key, PlayerIndex.One, out playerIndex) ||
                    IsNewKeyPress(key, PlayerIndex.Two, out playerIndex) ||
                    IsNewKeyPress(key, PlayerIndex.Three, out playerIndex) ||
                    IsNewKeyPress(key, PlayerIndex.Four, out playerIndex));
            }
        }

        /// <summary>
        /// Check to see if a new button was pressed during the current Update.
        /// controllingPlayer specifies which player is checked for Input.
        /// playerIndex specifies which player pressed it.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="controllingPlayer"></param>
        /// <param name="playerIndex"></param>
        /// <returns></returns>
        public bool IsNewButtonPress(Buttons button, PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
        {
            if (controllingPlayer.HasValue)
            {
                //Read Input from the Specified Controlling Player
                playerIndex = controllingPlayer.Value;

                int i = (int)playerIndex;

                return (mCurrentGamePadStates[i].IsButtonDown(button) &&
                    mCurrentGamePadStates[i].IsButtonUp(button));
            }
            else
            {
                //Otherwise, we can accept Input from any Player
                return (IsNewButtonPress(button, PlayerIndex.One, out playerIndex) ||
                    IsNewButtonPress(button, PlayerIndex.Two, out playerIndex) ||
                    IsNewButtonPress(button, PlayerIndex.Three, out playerIndex) ||
                    IsNewButtonPress(button, PlayerIndex.Four, out playerIndex));
            }
        }
        #endregion

        #region High Level Checks

        /// <summary>
        /// Checks to see if a Menu Option has been Selected
        /// A Menu Option can be selected via the following:
        ///  - Enter on Keyboard
        ///  - A Button on GamePad
        /// </summary>
        /// <param name="controllingPlayer"></param>
        /// <param name="playerIndex"></param>
        /// <returns></returns>
        public bool IsMenuOptionSelected(PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
        {
            return IsNewKeyPress(Keys.Enter, controllingPlayer, out playerIndex) ||
                IsNewButtonPress(Buttons.A, controllingPlayer, out playerIndex);
        }

        /// <summary>
        /// Checks to see if a Menu Option has been Canceled
        /// A Menu Option can be canceled via the following:
        ///  - Escape on Keyboard
        ///  - B Button on GamePad
        /// </summary>
        /// <param name="controllingPlayer"></param>
        /// <param name="playerIndex"></param>
        /// <returns></returns>
        public bool IsMenuOptionCancel(PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
        {
            return IsNewKeyPress(Keys.Escape, controllingPlayer, out playerIndex) ||
                IsNewButtonPress(Buttons.B, controllingPlayer, out playerIndex);
        }

        /// <summary>
        /// Check to see if we are Moving Up the Menu
        /// A Menu Up command can be detected via the following:
        ///  - Up Arrow on Keyboard
        ///  - DPad Up on GamePad
        ///  - Left Thumb Stick Up on GamePad
        /// </summary>
        /// <param name="controllingPlayer"></param>
        /// <param name="playerIndex"></param>
        /// <returns></returns>
        public bool IsMenuOptionUp(PlayerIndex? controllingPlayer)
        {
            PlayerIndex playerIndex;

            return IsNewKeyPress(Keys.Up, controllingPlayer, out playerIndex) ||
                IsNewButtonPress(Buttons.DPadUp, controllingPlayer, out playerIndex) ||
                IsNewButtonPress(Buttons.LeftThumbstickUp, controllingPlayer, out playerIndex);
        }

        /// <summary>
        /// Check to see if we are Moving Down the Menu
        /// A Menu Down command can be detected via the following:
        ///  - Up Arrow on Keyboard
        ///  - DPad Up on GamePad
        ///  - Left Thumb Stick Up on GamePad
        /// </summary>
        /// <param name="controllingPlayer"></param>
        /// <param name="playerIndex"></param>
        /// <returns></returns>
        public bool IsMenuOptionDown(PlayerIndex? controllingPlayer)
        {
            PlayerIndex playerIndex;

            return IsNewKeyPress(Keys.Down, controllingPlayer, out playerIndex) ||
                IsNewButtonPress(Buttons.DPadDown, controllingPlayer, out playerIndex) ||
                IsNewButtonPress(Buttons.LeftThumbstickDown, controllingPlayer, out playerIndex);
        }

        /// <summary>
        /// Checks to see if the Game has been Paused
        /// A Pause command can be detected via the following:
        ///  - Escape on Keyboard
        ///  - Start on GamePad
        /// </summary>
        /// <param name="controllingPlayer"></param>
        /// <param name="playerIndex"></param>
        /// <returns></returns>
        public bool IsPause(PlayerIndex? controllingPlayer, out PlayerIndex playerIndex)
        {
            return IsNewKeyPress(Keys.Escape, controllingPlayer, out playerIndex) ||
                IsNewButtonPress(Buttons.Start, controllingPlayer, out playerIndex);
        }

        #endregion
    }
}
